﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

using System.Net;           //支持网络
using System.Net.Sockets;   //支持socket
using System.Threading;     //支持多线程

namespace ChessServer
{
    //客户端状态类型枚举
    public enum ClientState
    { 
        离线 = 1, 空闲 = 2, 下挑战书 = 3, 接挑战书 = 4, 对弈 = 5
    }
    public partial class FormMain : Form
    {
        //服务器ip，端口
        IPAddress _serverIP = IPAddress.Parse("0.0.0.0");
        int _serverPort = 0;

        //服务器端Socket对象，用于监听客户端连接
        private Socket _socketListen = null;
        //list泛型对象，用于保存客户端的Socket对象
        private List<Socket> _listClient = new List<Socket>();

        //线程对象，用于服务器端的Socket监听客户端连接
        private Thread threadListConnect = null;

        //线程对象，用于服务器接收客户端数据包
        private Thread threadReceivePacket = null;

        //服务器点对点发送数据包到客户端
        public void ServerRelayPtpPakct(string content, IPAddress fromIp, int fromPort, IPAddress toIp, int toPort)
        {
            //对_listClient进行遍历
            for (int i = 0; i <= _listClient.Count - 1; i++)
            {
                //如果_listClient[i]不为空，且最近一次操作为连接
                if (_listClient[i] != null && _listClient[i].Connected == true)
                {
                    try
                    {
                        //获取_listClient[i]的远程ip，端口号
                        IPAddress ip = ((IPEndPoint)_listClient[i].RemoteEndPoint).Address;
                        int port = ((IPEndPoint)_listClient[i].RemoteEndPoint).Port;
                        //判断_listClient[i]的ip和端口是否和目的ip端口一样
                        if (ip.Equals(toIp) == true && port == toPort)
                        {

                            //数据包封包
                            string sendPacket = fromIp.ToString() + "-" + fromPort.ToString() + "-" + toIp.ToString() +
                                                "-" + toPort.ToString() + "-" + content;
                            //MessageBox.Show(this, "收到数据包：" + sendPacket, "数据包", MessageBoxButtons.OK, MessageBoxIcon.Information);
                            //通过socket发送封装好的数据包
                            Byte[] bytePacket = System.Text.Encoding.UTF8.GetBytes(sendPacket);
                            _listClient[i].Send(bytePacket);
                        }
                    }
                    catch (Exception excep)
                    {
                        MessageBox.Show(this, excep.Message, "异常", MessageBoxButtons.OK, MessageBoxIcon.Information);
                    }
                }
            }
        }

        //服务器广播消息到所有客户端
        public void ServerRelayBroadcastPacket(string content, IPAddress fromIp, int fromPort)
        { 
            //对_listClient进行遍历
            for (int i = 0; i <= _listClient.Count - 1; i++)
            { 
                //如果_listClient不为空，且最后一次操作为连接状态
                if (_listClient[i] != null && _listClient[i].Connected == true)
                {
                    try
                    {
                        //获取_listClient[i]的远程端ip，端口号
                        IPAddress ip = ((IPEndPoint)_listClient[i].RemoteEndPoint).Address;
                        int port = ((IPEndPoint)_listClient[i].RemoteEndPoint).Port;
                        //判断是否是数据包发送源，即不要往数据包源发送消息
                        if (fromIp.Equals(ip) == false || fromPort != port)
                        {
                            //数据封包
                            string sendPacket = fromIp.ToString() + "-" + fromPort.ToString() + "-" + ip.ToString() +
                                                "-" + port.ToString() + "-" + content;
                            //通过socket发送封装好的数据
                            Byte[] bytePacket = System.Text.Encoding.UTF8.GetBytes(sendPacket);
                            _listClient[i].Send(bytePacket);
                        }
                    }
                    catch (Exception excep)
                    {
                        MessageBox.Show(this, excep.Message, "异常", MessageBoxButtons.OK, MessageBoxIcon.Information);
                    }
                }
            }
        }

        //数据拆包（解析出ip，端口，内容）
        public bool DecodePacket(string packet, out IPAddress fromIp, out int fromPort, out IPAddress toIp, out int toPort,
                                 out string content)
        { 
            //查找前四个破折号“-”的位置
            int count = 0;
            int[] pos = new int[4];
            
            for (int i = 0; i <= packet.Length - 1; i++)
            {
                if (packet[i] == '-')
                { 
                    //保存破折号所在的位置
                    pos[count] = i;
                    count++;
                    //找到是个破折号就不再找了
                    if (count >= 4)
                        break;
                }
            }
            //判断破折号是否最够四个
            if (count == 4)
            {
                try
                {
                    //解析出数据包的ip和端口号
                    fromIp = IPAddress.Parse(packet.Substring(0, pos[0] - 0));
                    fromPort = Convert.ToInt32(packet.Substring(pos[0] + 1, pos[1] - pos[0] - 1));
                    toIp = IPAddress.Parse(packet.Substring(pos[1] + 1, pos[2] - pos[1] - 1));
                    toPort = Convert.ToInt32(packet.Substring(pos[2] + 1, pos[3] - pos[2] - 1));
                    //解析出数据包中的内容
                    content = packet.Remove(0, pos[3] + 1);
                    return true;
                }
                catch (Exception excep)
                {
                    fromIp = IPAddress.Parse("0.0.0.0");
                    fromPort = 0;
                    toIp = IPAddress.Parse("0.0.0.0");
                    toPort = 0;
                    content = "";
                    return false;
                }
            }
            else
            {
                fromIp = IPAddress.Parse("0.0.0.0");
                fromPort = 0;
                toIp = IPAddress.Parse("0.0.0.0");
                toPort = 0;
                content = "";
                return false;
            }
        }
        
        //移除listViewClient中一行客户端
        public void RemoveItemFromListViewClient(IPAddress ip, int port)
        { 
            //对listviewclient进行遍历
            for (int i = 0; i <= listViewClient.Items.Count - 1; i++)
            { 
                //如果是待移除的客户端，则从listviewClient中移除该行
                if (listViewClient.Items[i].SubItems[1].Text == ip.ToString()
                   && listViewClient.Items[i].SubItems[2].Text == port.ToString())
                {
                    listViewClient.Items.RemoveAt(i);
                    return;
                }
            }
        }
        
        //往listviewclient中添加一行客户端
        public void AddItemToListViewClient(string nickName, IPAddress ip, int port, ClientState state)
        { 
            //对listViewClient进行遍历， 如果存在该客户端，则直接更新
            for (int i = 0; i <= listViewClient.Items.Count - 1; i++)
            { 
                //如果是待更新的客户行
                if (listViewClient.Items[i].SubItems[1].Text == ip.ToString()
                   && listViewClient.Items[i].SubItems[2].Text == port.ToString())
                {
                    //修改昵称列和状态列信息
                    listViewClient.Items[i].Text = nickName;

                    listViewClient.Items[i].SubItems[3].Text = state.ToString();
                    return;
                }
            }
            //设置item和子item的值
            ListViewItem item = new ListViewItem(nickName);
            item.SubItems.Add(ip.ToString());
            item.SubItems.Add(port.ToString());
            item.SubItems.Add(state.ToString());

            //往listviewclient中添加一行item
            listViewClient.Items.Add(item);
        }
        
        //移除_listclient中一个socket的节点
        public void RemoveNodeFromListClient(Socket socket)
        { 
            //对_listClient进行遍历
            for (int i = _listClient.Count - 1; i >= 0; i--)
            { 
                //如果是待移除的socket节点
                if (_listClient[i] == socket)
                {
                    try
                    {
                        //关闭该socket
                        if (_listClient[i] != null)
                            _listClient[i].Close();
                        //移除该socket
                        _listClient.RemoveAt(i);
                        return;
                    }
                    catch (Exception excep)
                    {
                        MessageBox.Show(this, excep.Message, "异常", MessageBoxButtons.OK, MessageBoxIcon.Information);
                    }
                }
            }
        }
        
        //更新一行listviewclient状态
        public void UpdateItemOfListViewClient(IPAddress ip, int port, ClientState newState)
        { 
            //遍历
            for (int i = 0; i <= listViewClient.Items.Count - 1; i++)
            { 
                //如果是待更新的行
                if (listViewClient.Items[i].SubItems[1].Text == ip.ToString()
                   && listViewClient.Items[i].SubItems[2].Text == port.ToString())
                {
                    listViewClient.Items[i].SubItems[3].Text = newState.ToString();
                    return;
                }
            }
        }
        public FormMain()
        {
            InitializeComponent();

            //在comboBoxServerIP下拉列表中显示本机所有的IP地址
            IPHostEntry ipHostEntry = Dns.GetHostEntry(Dns.GetHostName());
            comboBoxServerIP.DataSource = ipHostEntry.AddressList;

            //在comboBoxServerIP中显示出本机的第一个ip地址
            comboBoxServerIP.Text = ipHostEntry.AddressList[0].ToString();
        }

        private void buttonStartListen_Click(object sender, EventArgs e)
        {
            try
            { 
                //创建服务器监听Socket
                _socketListen = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                //获取服务器端ip，端口号
                _serverIP = IPAddress.Parse(comboBoxServerIP.Text); _serverPort = Convert.ToInt32(numericUpDownListenPort.Text);
                //创建终结点
                IPEndPoint ipEndPoint = new IPEndPoint(_serverIP, _serverPort);
                //对监听socket进行绑定
                _socketListen.Bind(ipEndPoint);

                //创建一个serverListConnet线程，用于监听客户端连接
                threadListConnect = new Thread(ServerListenConnect);
      
                //使该线程在后台运行
                threadListConnect.IsBackground = true;
                //启动线程
                threadListConnect.Start();

                //修改按钮标题，并启动和禁用有关控件
                buttonStartListen.Text = "监听中...";
                comboBoxServerIP.Enabled = false; numericUpDownListenPort.Enabled = false; buttonStartListen.Enabled = false;
            }
            catch(Exception excep)
            {
                MessageBox.Show(this, excep.Message, "异常", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
        }
        private void ServerListenConnect()
        {
            while (true)
            {
                try
                { 
                    //服务器端Socket开始监听客户端连接
                    _socketListen.Listen(20);
                    //服务器接受一个客户端连接请求，并返回一个socket对象
                    Socket socket = _socketListen.Accept();
                    //添加到列表中
                    _listClient.Add(socket);
                    //弹出消息框
                    //IPAddress ip = ((IPEndPoint)socket.RemoteEndPoint).Address;
                    //int port = ((IPEndPoint)socket.RemoteEndPoint).Port;
                    //MessageBox.Show(this, ip.ToString() + "已经通过" + port.ToString() + "端口连接到服务器!",
                    //                                    "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);

                    //创建一个ServerReceivePacket线程，用于接受客户端发送的数据包
                    threadReceivePacket = new Thread(ServerReceivePacket);
                    //使线程在后台运行
                    threadReceivePacket.IsBackground = true;
                    //启动该线程
                    threadReceivePacket.Start(socket);
                }
                catch (Exception excep)
                {
                    MessageBox.Show(this, excep.Message, "异常", MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
            }
        }
        private void ServerReceivePacket(object objSocket)
        {
            while (true)
            { 
                //利用死循环不断接受数据包
                try
                {
                    //获取线程中的object参数对象值
                    Socket socket = (Socket)objSocket;
                    //判断socket是否处于连接状态
                    if (socket.Connected == true)
                    {
                        //创建存放数据包的字节数组缓冲区
                        Byte[] bytePacket = new Byte[4000];
                        //接受客户端传来的数据包
                        int length = socket.Receive(bytePacket);
                        //把Byte[]转换为string
                        String receivePacket = System.Text.Encoding.UTF8.GetString(bytePacket, 0, length);
                        //往listViewPacket中添加一条item
                        ListViewItem item = new ListViewItem(receivePacket);
                        listViewPacket.Items.Add(item);
                        //对数据进行拆包，解析出ip，端口号，内容
                        IPAddress fromIp, toIp; int fromPort, toPort; string content = "";
                        bool result = DecodePacket(receivePacket, out fromIp, out fromPort, out toIp, out toPort, out content);
                        //如果拆包成功
                        if (result == true)
                        { 
                            //根据目标ip，端口，进行接收，广播，转发
                            if (toIp.Equals(_serverIP) == true && toPort == _serverPort)
                                ;
                            else if (toIp.Equals(IPAddress.Parse("255.255.255.255")) == true)
                                //服务器转发广播数据到所有客户端
                                ServerRelayBroadcastPacket(content, fromIp, fromPort);
                            else
                                
                                //服务器转发数据到目的客户端
                                ServerRelayPtpPakct(content, fromIp, fromPort, toIp, toPort);
                            //判断数据包所含的指令
                            if (content.IndexOf("新人报到-") == 0)
                            { 
                                //解析出对方客户端的昵称
                                string nickName = content.Remove(0, 5);
                                //往listviewclient添加一行客户端
                                AddItemToListViewClient(nickName, fromIp, fromPort, ClientState.空闲);
                                //往listViewClient中添加客户端消息
                                //ListViewItem item2 = new ListViewItem(nickName);
                                //item2.SubItems.Add(fromIp.ToString());
                                //item2.SubItems.Add(fromIp.ToString());
                                //listViewClient.Items.Add(item2);//往listViewClient中添加item
                            }
                            else if (content.IndexOf("我开溜了-") == 0)
                            {
                                //移除listviewclient的一行客户端
                                RemoveItemFromListViewClient(fromIp, fromPort);
                                //移除_listClient中与socket对应的节点
                                RemoveNodeFromListClient(socket);
                                //退出线程
                                return;
                            }
                            else if (content.IndexOf("状态通报-") == 0)
                            {
                                //解析出对方客户端的状态
                                ClientState state = (ClientState)Enum.Parse(typeof(ClientState), content.Remove(0, 5));
                                //更新客户端状态
                                UpdateItemOfListViewClient(fromIp, fromPort, state);
                            }
                        }
                        //通过消息框显示接收到的数据包
                        //MessageBox.Show(this, "收到数据包：" + receivePacket, "数据包", MessageBoxButtons.OK, MessageBoxIcon.Information);
                    }
                    else
                        return;
                }
                catch (Exception excep)
                { 
                    MessageBox.Show(this, excep.Message, "异常", MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
            }
        }
    }
}
